package ch01.text00.tx08.demo03;

import ch01.text00.tx10.SqlGenerate;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.Map;

/**
 * @author V
 * @date 2025-03-21
 */
public class DbUtil {
    /**
     * 条件查询某张数据表
     *
     * @param beanClass 实体对象 - 映射某张数据表
     * @param cond      条件
     * @return 初始化的实体对象
     */
    public static Object select4Bean(Class<?> beanClass, Map<String, Object> cond) {
        // 第一：定义要操作数据库的 SQL 语句
        String sql = SqlGenerate.generateSelect(beanClass, cond);

        // 第二：获取连接对象
        Connection conn = DruidUtil.getConnection();

        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            // 第三：预编译 SQL 语句
            preparedStatement = conn.prepareStatement(sql);

            // 第四：填充参数
            setParameter(cond, preparedStatement);

            // 第五：执行查询操作 - resultSet 中，存储着当前查询的数据
            resultSet = preparedStatement.executeQuery();

            // 第六：把结果集中的数据映射到实体对象中，并返回 - 基于反射（内省）技术，实现对象的映射
            if(resultSet.next()) {
                // 1.获取元数据对象 - 获取结果集的相关信息
                ResultSetMetaData metaData = resultSet.getMetaData();

                // 2.基于元数据对象获取总列数
                int columnCount = metaData.getColumnCount();

                // 3.实例化 Bean 对象
                Object bean = beanClass.getConstructor().newInstance();


                // 4.循环遍历各列
                for (int i = 1; i <= columnCount; i++) {
                    // 5.基于元数据对象，获取查询表的列名称 - sta_pos -> staPos
                    String columnLabel = metaData.getColumnLabel(i);

                    // 6.根据字段名称，获取各列数据
                    Object data = resultSet.getObject(columnLabel);

                    // 7.基于数据表字段名称，通过反射获取实体对象对应 Field 对象
                    Field field = beanClass.getDeclaredField(SqlGenerate.convertToCamel(columnLabel));

                    // 判断 Bean 对象的当前字段是否为基本类型，并且获取数据表的数据为
                    if(field.getType().isPrimitive() && data == null) {
                        continue;
                    }

                    // 8.初始化 Bean 对象 - 把查询的字段数据映射到Bean对象相应的属性中
                    field.setAccessible(true);
                    field.set(bean,data);
                }

                // 9.返回初始化的Bean对象
                return bean ;
            }
        } catch (Exception e) {
            throw new RuntimeException("查询失败!", e);
        } finally {
            // 关闭对象
            DruidUtil.close(resultSet, preparedStatement, conn);
        }

        return null;
    }

    /**
     * 填充 SQL 占位符
     *
     * @param cond              条件数据
     * @param preparedStatement 语句对象
     * @throws SQLException 异常
     */
    private static void setParameter(Map<String, Object> cond, PreparedStatement preparedStatement) throws SQLException {
        int i = 1;
        for (Map.Entry<String, Object> entry : cond.entrySet()) {
            preparedStatement.setObject(i++, entry.getValue());
        }
    }
}

